/*
 * Copyright (C) 2009 Anatoly Zenkov
 *
 * This software is provided 'as-is', without any express or implied
 * warranty.  In no event will the author be held liable for any damages
 * arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must not
 *    claim that you wrote the original software. If you use this software
 *    in a product, an acknowledgment in the product documentation would be
 *    appreciated but is not required.
 * 2. Altered source versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 */
package com.kartoshka.utils {

	/**
	 * @author AnatolyZenkov
	 */
	public class MathUtil {
		/**
		 * Half of PI number.
		 */		
		public static const HALF_PI : Number = Math.PI * .5;
		/**
		 * Quarter of PI number. 
		 */
		public static const THIRD_PI : Number = Math.PI / 3;
		/**
		 * Third part of PI number. 
		 */		
		public static const QUART_PI : Number = Math.PI * .25;
		/**
		 * Doubled PI number.
		 */
		public static const DOUBLE_PI : Number = Math.PI * 2;

		/**
		 * Determines a number between 0 and 1 that represent
		 * the smooth transformation of value of parameter <code>n</code>
		 * (<code>n</code> located between 0 and 1).
		 * This function gives relaxed (curved) transformations of output value
		 * when <code>n</code> value straight growing from 0 and to 1 ("speed up"
		 * and "slow down" effect).
		 * Very useful for smooth transformation of any numerical parameter
		 * between two given values by linear increasing of another parameter.
		 * <img src="smooth.gif">
		 * 
		 * @param n Input parameter (<code>0&lt;=n&lt;=1</code>).
		 * @param f Strength of curving. Positive number. Minimum value is 0.6.
		 * 
		 * @return Number between 0 and 1. Very close to <code>n</code>,
		 * when <code>f=0.6</code>.
		 * 
		 * @see #speedUp()
		 * @see #slowDown()
		 */
		public static function smooth(n : Number, f : Number = 1) : Number {
			f = Math.max(0.6, f);
			if (n < .5) return Math.pow(1 - Math.cos(Math.PI * n), f) * .5;
			return .5 + (1 - Math.pow(1 - Math.cos(Math.PI * (1 - n)), f)) * .5;
		}

		/**
		 * Determines a number between 0 and 1 that represent
		 * the smooth rising of value of parameter <code>n</code>
		 * (<code>n</code> located between 0 and 1).
		 * This function gives smooth increasing of output value
		 * when <code>n</code> value straight growing from 0 ("speed up" effect).
		 * Very useful for smooth transformation of any numerical parameter
		 * between two given values by linear increasing of another parameter.
		 * <img src="speedup.gif">

		 * @param n Input parameter (<code>0&lt;=n&lt;=1</code>).
		 * @param f Strength of curving. Positive number. Minimum value is 0.6.
		 * 
		 * @return Number between 0 and 1. Very close to <code>n</code>,
		 * when <code>f=0.6</code>.
		 * 
		 * @see #smooth()
		 * @see #slowDown()
		 */
		public static function speedUp(n : Number, f : Number = 1) : Number {
			f = Math.max(0.6, f);
			return Math.pow(1 - Math.cos(HALF_PI * n), f);
		}

		/**
		 * Determines a number between 0 and 1 that represent
		 * the smooth slowing of value of parameter <code>n</code>
		 * (<code>n</code> located between 0 and 1).
		 * This function gives smooth increasing of output value
		 * when <code>n</code> value straight growing to 1 ("slow down" effect).
		 * Very useful for smooth transformation of any numerical parameter
		 * between two given values by linear increasing of another parameter.
		 * <img src="slowdown.gif">
		 * 

		 * @param n Input parameter (<code>0&lt;=n&lt;=1</code>).
		 * @param f Strength of curving. Positive number. Minimum value is 0.6.
		 * 
		 * @return Number between 0 and 1. Very close to <code>n</code>,
		 * when <code>f=0.6</code>.
		 * 
		 * @see #smooth()
		 * @see #speedUp()
		 */		
		public static function slowDown(n : Number, f : Number = 1) : Number {
			f = Math.max(0.6, f);
			return 1 - Math.pow(1 - Math.cos(HALF_PI * (1 - n)), f);
		}
		/**
		 * 
		 * AS2 styled random() function. Returns random integer number from the range
		 * between 0 and given positive number (parameter <code>a</code>).
		 * @param a Positive number. Upper limit of range.
		 * @return Random integer number. It is equal or greather than
		 * zero, but always less than given value (parameter <code>a</code>).
		 */
		public static function random(a:uint):int
		{
			return int(Math.random()*a);
		}

		/**
		 * 
		 * Returns random number from the range between given numbers
		 * <code>a</code> and <code>b</code> as limits of the range. 
		 * @param a First limit of range.
		 * @param b Second limit of range.
		 * @return Random number from the range. May be equal to value
		 * of parameter <code>a</code>, but never equals
		 * to value of parameter <code>b</code>.
		 */
		public static function randomRange(a:Number, b:Number):Number
		{
			return a + (b-a)*Math.random();
		}
		/**
		 * Determines a number between two specified numbers.
		 * The parameter <code>f</code> determines where the new interpolated number
		 * is located relative to the two end numbers specified by parameters
		 * <code>a</code> and <code>b</code>.
		 * The closer the value of the parameter <code>f</code> is to 0,
		 * the closer the interpolated number is to the first number (parameter <code>a</code>).
		 * The closer the value of the parameter <code>f</code> is to 1.0,
		 * the closer the interpolated number is to the second number (parameter <code>b</code>).
		 * 
		 * @param a First end number.
		 * @param b Second end number.
		 * @param f The level of interpolation between the two end numbers.
		 * Indicates where the new number will be, along the line between
		 * <code>a</code> and <code>b</code>. If <code>f=0</code>, <code>a</code> is returned;
		 * if <code>f=1</code>, <code>b</code> is returned.
		 * 
		 * @return The new, interpolated number.
		 */
		public static function interpolate (a:Number, b:Number, f:Number):Number
		{
			return a + (b-a)*f;
		}
		/**
		 * Returns number 1 with sign of given parameter <code>n</code>.
		 * Returns 1 when <code>n</code> is positive and -1 when <code>n</code>
		 * is negative. If <code>n=0</code> it returns <code>undefinded</code>.
		 * @param n
		 * @return -1 or 1, depends on sign of given parameter <code>n</code>.
		   <code>undefined</code> when <code>n=0</code>.
		 * 
		 */		
		public static function sign(n:Number):int
		{
			return !n ? 0 : n/Math.abs(n);
		}
		public static function clamp(n:Number, min:Number, max:Number):Number
		{
			return min < max ? Math.max(Math.min(max, n), min) : Math.max(Math.min(min, n), max);
		}
		public static function ratio(n:Number, min:Number, max:Number):Number
		{
			return min < max ? (n-min)/(max-min) : (n-max)/(min-max);
		}
		public static function correctAngle(a0:Number, a1:Number):Number
		{
			var d:Number = a1-a0;
			if (d>HALF_PI) return d % DOUBLE_PI;
			if (d<-HALF_PI) return d % (-DOUBLE_PI);
			return d;
		}
	}
}
